<?php
require_once dirname(__FILE__) . '/binder.php';
/**
 * 保管「ディスクドライブ」クラス
 */
require_once dirname(__FILE__) . '/storage.php';
/**
 * 保管「ディスクドライブ」クラス
 * アプリケーション名とラベルからファイル名を生成し
 * 指定フォルダーにファイルを生成する
 * 保管時にシリアライズし、取り出す際に複合化する
 */
class Storage_Diskunit extends Storage
{
	const KEY_APPLICATION_NAME = 'application_name';
	const KEY_LABEL = 'label';
	/** ディレクトリパス */
	protected $_path;
	/** ロックファイル名 */
	protected $_lockFileName;
	/**
	 * コンストラクタ
	 * @param $applicationName
	 * @param $path
	 */
	public function __construct($applicationName, $path)
	{
		if (substr($path,-1) != '/') {
			$path = $path . '/';
		}
		$this->_path = $path;
		parent::__construct($applicationName);
	}

	/**
	 * (non-PHPdoc)
	 * @see Storage::needRock()
	 */
	public function needLock($label)
	{
		$this->_lockFileName = $this->l2p($label) . '.lock';
		return TRUE;
	}
	/**
	 * (non-PHPdoc)
	 * @see Storage::lock()
	 */
	public function lock()
	{
		/*** すでにロックされている場合はFALSE */
		if ($this->isLock()) {
			return FALSE;
		}
		/*** ロックファイルの設置に失敗したら FASLE */
		if (file_put_contents($this->_lockFileName, '') === FALSE) {
			return FALSE;
		}
		return TRUE;
	}

	/**
	 * (non-PHPdoc)
	 * @see Storage::isLock()
	 */
	public function isLock()
	{
		if (file_exists($this->_lockFileName)) {
			return TRUE;
		}
		return FALSE;
	}

	/**
	 * (non-PHPdoc)
	 * @see Storage::unLock()
	 */
	public function unLock()
	{
		if ($this->isLock()){
			return  unlink($this->_lockFileName);
		}
		return TRUE;
	}

	/**
	 * 取り出し
	 * @see Storage::fetch()
	 */
	public function fetch($label)
	{
		$pysicalName = $this->l2p($label);
		/*** ファイルがなければＦＡＬＳＥ */
		if (!file_exists($pysicalName)) {
			return FALSE;
		}
		$serial = file_get_contents($pysicalName);
		$object = unserialize($serial);
		return $object;
	}

	/**
	 * 保管(non-PHPdoc)
	 * @see Storage::store()
	 */
	public function store($label, $value)
	{
		$pysicalName = $this->l2p($label);
		$serial = serialize($value);
		file_put_contents($pysicalName, $serial);
	}

	/**
	 * ファイル名用に文字列をＨＥＸ形式に変換
	 * @param unknown $label
	 * @return string
	 */
	protected function l2p($label)
	{
		return $this->_path . bin2hex($this->_applicationName . '_' . $label);
	}

	/**
	 * メンバーディレクトリをスキャンしてラベルバインダーを返す
	 * @param Storage_Diskunit_ScanFilter $fileter
	 */
	public function scandir(Storage_Diskunit_ScanFilter $filter)
	{
		$labels = new Binder('scandir');
		$files = scandir($this->_path);
		/*** スキャンファイルをすべて確認する */
		foreach ($files as $file) {
			/*** ディレクトリならスキップ */
			if (is_dir($this->_path . $file)) {
				continue;
			}
			/** ファイル名からターゲットファイル判定を実施する */
			$fileObject = $this->_scandir_isTarget($file, $filter);
			/***  ターゲットではないと判定された場合スキップする */
			 if ($fileObject === FALSE) {
			 	continue;
			 }
			 $labels->bind($fileObject);
		}
		return $labels;
	}

	/**
	 * ターゲットのファイルであればオブジェクトを返す
	 * @param unknown $fileName
	 * @param Storage_Diskunit_ScanFilter $filter
	 */

	/**
	 * ターゲットのファイルであればオブジェクトを返す
	 * @param unknown $fileName
	 * @param Storage_Diskunit_ScanFilter $filter
	 * @return boolean|object
	 */
	protected function _scandir_isTarget($fileName , Storage_Diskunit_ScanFilter $filter)
	{
		/*** 物理ファイル名をラベルとアプリケーション名にデコードする */
		$decode = @hex2bin($fileName);
		/*** デコードできない場合ターゲットではないと断定*/
		if ($decode === FALSE) {
			return FALSE;
		}
		/*** アプリケーション名とラベルに分離する */
		$divide = explode('_', $decode, 2);
		/*** 分離できなければターゲットではないと断定 */
		if (count($divide) != 2) {
			return FALSE;
		}

		$fileObject = new object();
		$fileObject->applicationName = $divide[0];
		$fileObject->label = $divide[1];

		/*** フィルターが設定されていない場合、ターゲットと断定 */
		if ($filter === NULL) {
			return $fileObject;
		}

		/*** アプリケーション名フィルターメソッドが設定されている場合、フィルタリング判定を実施する */
		if ($filter->applicationNameFilterMethod!= '') {
			/*** フィルタリング判定により該当のアプリケーション名でなければターゲットではないと断定する */
			if (!$fileObject->isYou('applicationName', $this->_applicationName, $filter->applicationNameFilterMethod)) {
				return FALSE;
			}
		}

		/*** ラベルフィルターが設定されている場合、フィルタリング判定を実施する */
		if ($filter->label != '') {
			/*** フィルタリング判定により該当のラベルでなければターゲットではないと判定する */
			if (!$fileObject->isYou('label', $filter->label, $filter->labelFilterMethod)) {
				return FALSE;
			}
		}

		/***  フィルターを通過のでターゲットと断定する */
		return $fileObject;
	}

}

/**
 * スキャンフィルタークラス
 */
class Storage_Diskunit_ScanFilter extends object
{
//	public $applicationName;
	public $applicationNameFilterMethod;
	public $label;
	public $labelFilterMethod;
}

/***************************************************************/
if (!function_exists('hex2bin')) {
	function hex2bin($data) {
		static $old;
		if ($old === null) {
			$old = version_compare(PHP_VERSION, '5.2', '<');
		}
		$isobj = false;
		if (is_scalar($data) || (($isobj = is_object($data)) && method_exists($data, '__toString'))) {
			if ($isobj && $old) {
				ob_start();
				echo $data;
				$data = ob_get_clean();
			}
			else {
				$data = (string) $data;
			}
		}
		else {
			trigger_error(__FUNCTION__.'() expects parameter 1 to be string, ' . gettype($data) . ' given', E_USER_WARNING);
			return;//null in this case
		}
		$len = strlen($data);
		if ($len % 2) {
			trigger_error(__FUNCTION__.'(): Hexadecimal input string must have an even length', E_USER_WARNING);
			return false;
		}
		if (strspn($data, '0123456789abcdefABCDEF') != $len) {
			trigger_error(__FUNCTION__.'(): Input string must be hexadecimal string', E_USER_WARNING);
			return false;
		}
		return pack('H*', $data);
	}
}


/**
 * 単体テストコードブロック
 */
if (PATH_SEPARATOR == ';') {
include_once 'simpletest/unit_tester.php';
include_once 'simpletest/reporter.php';

if (class_exists('UnitTestCase')) {
class DiskunitTest extends UnitTestCase {
	/**
	 * ストレージなしのバインダーテスト
	 */
	public function test1000() {
		$obj = new Storage_Diskunit('app', 'C:\pleiades432\wks\hlib\CCV\templates_c');
		$filter = new Storage_Diskunit_ScanFilter();
//		$filter->applicationName = 'ccv';
		$filter->applicationNameFilterMethod = object::SELECT_AGREE;
		$ret = $obj->scandir($filter);
		print_r($ret);
	}
}
}
}